package com.tools.demo.login.secret;


import com.tools.demo.login.secret.exception.AesException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;

/**
 * AES加密对象
 * @author Haviven
 * @since 2021/9/16
 **/
public class AesKey {

    private final String aesKey;

    /**
     * 生成16位不重复的随机数，含数字+大小写
     */
    private String generateKey() {
        StringBuilder uid = new StringBuilder();
        //产生16位的强随机数
        Random rd = new SecureRandom();
        for (int i = 0; i < 16; i++) {
            //产生0-2的3位随机数
            int type = rd.nextInt(3);
            switch (type){
                case 0:
                    //0-9的随机数
                    uid.append(rd.nextInt(10));
                    break;
                case 1:
                    //ASCII在65-90之间为大写,获取大写随机
                    uid.append((char)(rd.nextInt(25)+65));
                    break;
                case 2:
                    //ASCII在97-122之间为小写，获取小写随机
                    uid.append((char)(rd.nextInt(25)+97));
                    break;
                default:
                    break;
            }
        }
        return uid.toString();
    }


    /**
     * 随机生成一个AesKey(16位)
     */
    public AesKey() {
        aesKey = generateKey();
    }

    /**
     * 指定一个aesKey生成Aes对象
     * @param aesKey 密钥
     */
    public AesKey(String aesKey) {
        if(aesKey.length() != 16) throw new AesException("key的长度必须为16位");

        this.aesKey = aesKey;
    }

    public String getAesKey() {
        return aesKey;
    }

    /**
     * 加密
     * @param content 加密内容
     * @return 加密后内容
     */
    public byte[] encrypt(byte[] content){
        try {
            byte[] raw = aesKey.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            return cipher.doFinal(content);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | InvalidKeyException | BadPaddingException e) {
            throw new AesException(e);
        }
    }

    /**
     * 加密
     * @param content 加密内容
     * @return 加密后内容
     */
    public String encrypt(String content) {
        try {
            return byte2hex(encrypt(content.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            throw new AesException(e);
        }
    }

    /**
     * 解密
     * @param content 解密内容
     * @return 解密后内容
     */
    public byte[] decrypt(byte[] content) {
        try {
            byte[] raw = aesKey.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            return cipher.doFinal(content);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new AesException(e);
        }
    }

    /**
     * 解密
     * @param content 解密内容
     * @return 解密后内容
     */
    public String decrypt(String content) {
        try {
            return new String(decrypt(hex2byte(content)), StandardCharsets.UTF_8);
        } catch (IllegalArgumentException e) {
            throw new AesException(e);
        }
    }


    private String byte2hex(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String tmp = "";
        for (byte value : b) {
            tmp = Integer.toHexString(value & 0xFF);
            if (tmp.length() == 1) {
                hs.append("0").append(tmp);
            } else {
                hs.append(tmp);
            }
        }
        return hs.toString().toUpperCase();
    }

    private byte[] hex2byte(String hex) throws IllegalArgumentException {
        if (hex.length() % 2 != 0) {
            System.out.println("hex:" + hex + "\nlength:" + hex.length());
            throw new IllegalArgumentException();
        }
        char[] arr = hex.toCharArray();
        byte[] b = new byte[hex.length() / 2];
        for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
            String swap = "" + arr[i++] + arr[i];
            int byteInt = Integer.parseInt(swap, 16) & 0xFF;
            b[j] = new Integer(byteInt).byteValue();
        }
        return b;
    }

    public String getKey() {
        return this.aesKey;
    }
}
